package com.ssy.lingxi.common.handler;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ssy.lingxi.common.annotation.Desensitized;

import java.io.IOException;
import java.util.Objects;

/**
 * 重写jackson的序列化方式, 将数据脱敏
 * TODO 可拓展其他脱敏方式, 邮箱、手机号等
 * @author yzc
 * @version 2.0.0
 * @date 2020/10/27
 */
public class DesensitizeSerialize extends JsonSerializer<String> implements ContextualSerializer {

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(this.desensitizeValue(value, "*"));
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        if (property != null) {
            if (Objects.equals(property.getType().getRawClass(), String.class)) {
                Desensitized desensitized = property.getAnnotation(Desensitized.class);
                if (desensitized == null) {
                    desensitized = property.getContextAnnotation(Desensitized.class);
                }
                if (desensitized != null) {
                    return new DesensitizeSerialize();
                }
            }
            return prov.findValueSerializer(property.getType(), property);
        }
        return prov.findNullValueSerializer(null);
    }


    /**
     * 数据脱敏
     * @param value 敏感信息字符串
     * @param mask 字符掩码
     * @return 掩码字符串
     */
    private String desensitizeValue(String value, String mask) {
        if (value == null) {
            return null;
        }

        // 不保留或保留左侧一个字符
        if (value.length() <= 2) {
            return desVal(value, 1, 0, mask);
        }

        // 保留左右两侧一个字符
        if (value.length() <= 5) {
            return desVal(value, 1, 1, mask);
        }

        // 保留左右两则两个字符
        return desVal(value, 2, 2, mask);
    }

    /**
     * 字符替换
     * @param val 明文
     * @param preLen 左侧保留明文长度
     * @param sufLen 右侧保留明文长度
     * @param mask 遮罩字符
     * @return 替换后的字符串
     */
    private String desVal(String val, int preLen, int sufLen, String mask) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0, n = val.length(); i < n; i++) {
            if (i < preLen) {
                sb.append(val.charAt(i));
                continue;
            }
            if (i > (n - sufLen - 1)) {
                sb.append(val.charAt(i));
                continue;
            }
            sb.append(mask);
        }

        return sb.toString();
    }
}
